# -*- coding: utf-8  -*-
#    Vandalism Finder Bot - A Python bot that uses Pywikipediabot
#    framework and works on MediaWiki wikis at general.
#
#    Copyright (C) 2009 OsamaK
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#    Please report bugs or help imporving this program by connecting to
#    <OsamaK.WFM@gmail.com>
import wikipedia, okbot
import time, sys, codecs
from datetime import datetime, timedelta

class VandalismBot:
  "الطبقة الأساسية للبوت"
  def __init__(self):
    self.site = wikipedia.getSite(fam='wikipedia', code='ar') # although it can be used directly on other wikis, but this is the default.
    self.rcstart = "1" # Should be '1' to scan the whole last month log.

  def run(self):
    "دالة بدء البوت"
    while True:
      ignoredUsers = self.getIgnored()
      pages = self.getRC()
      logfile = codecs.open('vand.log','a','utf-8')
      for page in pages:
        user = page['user']
        title = page['title']
        rcid = page['rcid']
        pageObject = wikipedia.Page(self.site, title)
        pageTest = self.isVandel(pageObject, page['user'])
        if pageTest: # Page is not OK, check the user if they are not too.
          if not user in ignoredUsers: # User is not OK as well, page should be deleted.
            wikipedia.output("%s (created by %s) should be speedy deleted! <http://%s%s?title=%s&rcid=%s>" % (title, user, self.site.hostname(), self.site.path(), pageObject.urlname(), rcid))
            self.addReport(pageObject, user, rcid, pageTest[1])
            if pageTest[0] != "nonuser creation": #Do not add deletion template on non-user-created pages, noting the user is enough.
              if self.pageTag(title, pageTest[1]) == False: continue
            self.userNotice(title, user, pageTest[0])
            self.patrol(rcid, pageObject)
          elif user in ignoredUsers: #Page is not OK, but the user is.
            wikipedia.output("The user %s is an ignored user" % user)
        elif not pageTest: # Page is OK.
          wikipedia.output("%s is OK." % title)
        if pageTest is None: # pageTest will throw an exception, as None[0] doesn't work.
          logfile.write(u"%s--%s--%s--%s--%s\n" % (title, user, rcid, pageTest, page['timestamp']))
        else:
          logfile.write(u"%s--%s--%s--%s--%s\n" % (title, user, rcid, pageTest[0], page['timestamp']))
      if pages: # if there are new pages (if the pages list isn't empty), add a second.
        self.addASecond(pages[-1]['timestamp'])
      logfile.close()

      wikipedia.output("Sleep for 30 seconds.")
      time.sleep(30) # Can be changed freely.

  def getIgnored(self):
    """
  يقوم البوت هنا باستيراد القائمة البيضاء من الويكي الحية، وتقسيم
  المستخدمين المدخلين فيها كعناصر قائمة، وإرجاع القائمة.
    """
    ignoredpage = wikipedia.Page(self.site, u"مستخدم:OsamaK/قائمة بيضاء")
    ignoredUsers = []
    try:
      for user in [line.lstrip(u"* [[مستخدم:").rstrip(u"]]") for line in ignoredpage.get().split('\n') if line.startswith(u"* [[مستخدم:") and line.endswith(u"]]")]:
        ignoredUsers.append(user)
    except wikipedia.NoPage:
      wikipedia.output("No ignored users page, skipped")
      return []

    printignored = "Ignored users are: "
    for user in ignoredUsers:
      printignored = printignored + user + " "

    wikipedia.output(printignored)
    return ignoredUsers

  def getRC(self):
    "اجلب آخر الصفحات وأرجعها في قائمة. أرجع قائمةً فارغةً إذا لم توجد أي صفحات جديدة"
    predata = {#api.php?action=query&list=recentchanges&rcdir=newer&rctype=new&rcnamespace=0|1|2|4|5|6|7|8|9|10|11|12|13|14|15|100|101&rcshow=!redirect|!bot|!patrolled&rcprop=user|ids|title|sizes|timestamp|sizes&format=jsonfm
               'action': 'query',
               'list': 'recentchanges',
               'rcdir': 'newer',
               'rctype': 'new',
               'rcnamespace': '0|1|2|4|5|6|7|8|9|10|11|12|13|14|15|100|101', # To ignore user talk pages, no tests are applied on them.
               'rcshow': '!redirect|!bot|!patrolled',
               'rcprop': 'user|ids|title|sizes|timestamp',
               'rclimit': '100',
               'rcstart': self.rcstart,
               'format': 'json',
                }

    while True:
      rcdata = okbot.getAPI(self.site, predata)
      if rcdata != None: break

    if len(rcdata['query']['recentchanges']) == 0:
      print "No items there!"

    pages = []
    for page in rcdata['query']['recentchanges']:
      pages.append(page)

    return pages

  def isVandel(self, page, user):
    """
  هذه الخاصية هي نصف اختبار كشف التخريب، جنبا إلى جنب مع اختبار المستخدم، يتم فيها
  استيراد 1) كائن الصفحة 2) طول الصفحة عند الإنشاء. 3) اسم المستخدم المنشئ.
  1- مستخدم أنشأ صفحة أقل من 100 بايت، تحتوي (نص تجربة، وصلة خارجية)، باستثناء نطاق المستخدم ونقاشه - تم
  2- مستخدم أنشأ صفحة أقل 100 وأكثر من 40 بثلاث كلمات أو أقل في نطاق المقالات. - تم!
  3- مستخدم أنشأ صفحة بأقل من 20 حرف في نطاق المقالات والنقاشات - تم!
  4- لو أنشأ مساهم غير مسجل صفحة مساهم مسجل آخر، سوف يقوم البوت بإخطار صاحب الصفحة
  الأصلي في صفحة نقاشه (فيما إذا كان هو أنشأئها) ويقوم بوضع قالب شطب. تم! جزئيا
    """
    try:
      pageText = page.get()
    except wikipedia.NoPage:
      wikipedia.output("Page does not exist, skipped.")
      return
    except KeyboardInterrupt:
      raise KeyboardInterrupt
    except:
      wikipedia.output("Unknown error, skipped.")
      return

    pagelen = len(pageText)

    for skipword in [u"شطب", u"حذف", u"خرق", u"بوت", u"bot", u"رأس صفحة نقاش", u"ليست منتدى"]:
      if pageText.lower().find(skipword) != -1: return

    if pagelen < 100 and (not page.namespace() in [2,3] or (page.namespace() == 2 and page.titleWithoutNamespace().split('/')[0] != user)):
      testbuttons=[
                   u"نص عريض",
                   u"نص مائل",
                   u"Example.jpg", u"Example.ogg",
                   u"أدخل الصيغة هنا",
                   u"أدخل النص غير المنسق هنا",
                   u"نص عنوان رئيسي",
                   u"عنوان وصلة",
                   "http://", "www",
                   u"{{بي_بي_سي||2=_}}", u"{{سي_إن_إن||2=_}}", u"{{الجزيرة||2=_}}", u"{{رويترز||2=_}}",u"{{تصنيف_كومنز|}}", u"#تحويل [[]]",
                   u"#تحويل [[المكان المقصود]]",
                   u"ملف:Computer n screen.svg|تعليق", u"ملف:Crystal Clear app lphoto.png|تعليق", u"ملف:Sciences de la terre.svg|تعليق"]
      for testbutton in testbuttons:
        if pageText.find(testbutton) != -1:
          return ("testbutton", u"صفحة تجربة")

    if pagelen >= 40 and len(pageText.split()) <= 3 and (page.namespace() == 0 or (page.namespace() == 2 and page.titleWithoutNamespace().split('/')[0] != user)):
      return ("long words", u"تخريب")

    if pagelen < 20 and (page.namespace() in [0, 1, 5, 7, 9, 11, 13, 15, 101] or (page.namespace() == 2 and page.titleWithoutNamespace().split('/')[0] != user)):
      return ("very light", u"قصيرة جدا")

    if page.namespace() == 2 and page.titleWithoutNamespace().split('/')[0] != user:
      return ("nonuser creation", u"ليست من إنشاء المستخدم")

  def pageTag(self, title, pageTest):
    "سِم الصفحة بقالب الشطب"
    taggingPage = wikipedia.Page(self.site, title)

    try: #Sometimes it gets removed very quickly, this won't make it crash.
      okbot.put_page(self.site, taggingPage, (u"{{شطب|%s}}\n\n" % pageTest) + taggingPage.get(), u"روبوت: صفحة تطابق معايير الشطب، %s" % pageTest)
    except wikipedia.NoPage:
      wikipedia.output("%s has been deleted, skipped." % title)
      return False
    except:
      wikipedia.output("Unknown error, skipped")
      return False

  def userNotice(self, title, user, pageTest):
    "أخطر المستخدم"
    if pageTest != "nonuser creation":
      usertalkpage = wikipedia.Page(self.site, (u"نقاش المستخدم:%s" % user))
    else:
      usertalkpage = wikipedia.Page(self.site, (u"نقاش المستخدم:%s" % title[7:].split('/')[0]))
    if usertalkpage.exists():
      talk_page_text = usertalkpage.get() + "\n\n"
    else:
      talk_page_text = u""
    okbot.put_page(self.site, usertalkpage, (talk_page_text + (u"{{نسخ:مستخدم:OsamaK/تنبيه الشطب|%s|%s}}--~~~~" % (pageTest, title))),(u"روبوت: الصفحة [[%s]] من المرجح أنها تخريب" % title))

  def addReport(self, pageObject, user, rcid, pageTest):
    "أضف مدخلة إلى سجل التخريب"
    ArabicMonths = [u"يناير", u"فبراير", u"مارس", u"أبريل", u"مايو", u"يونيو", u"يوليو", u"أغسطس", u"سبتمبر", u"أكتوبر", u"نوفمبر", u"ديسمبر"]
    reportTitle = u"مستخدم:OsamaK/كشف التخريب/" + ArabicMonths[time.gmtime()[1] - 1] + " " + str(time.gmtime()[0])
    reportPage = wikipedia.Page(self.site, reportTitle)
    try:
      reportText = reportPage.get()
    except wikipedia.NoPage:
      reportText = ""
    if reportText == "" or reportText.find(u"<!---الجديد أعلاه--->") == -1:
      reportText = u"{{مستخدم:OsamaK/كشف التخريب/ترويسة}}\n\n{| class=\"wikitable\" style=\"width:75%\"\n|-\n! التاريخ\n! المقالة\n! المستخدم/الآيبي المنشئ\n! سبب الحذف\n<!---الجديد أعلاه--->\n|}"
    reportText = reportText.replace(u"<!---الجديد أعلاه--->", (u"{{مستخدم:OsamaK/كشف التخريب/قالب\n|التاريخ=~~~~~\n|الاسم=%s\n|السبب=%s\n|المستخدم=%s\n}}\n<!---الجديد أعلاه--->" % (pageObject.title(), pageTest, user)))

    okbot.put_page(self.site, reportPage, reportText, (u"روبوت: إضافة مدخلة [[%s]] إلى سجل كشف التخريب" % pageObject.title()))

  def patrol(self, rcid, pageObject):
    "سِم الصفحة كمراجعة"
    token = self.site.getToken(pageObject)
    predata = {
              'action': 'patrol',
              'token': token,
              'rcid': str(rcid),
              'format': 'json',
              }
    okbot.getAPI(self.site, predata)

  def addASecond(self, rcstart):
    mwTimeFormat ="%Y-%m-%dT%H:%M:%SZ" # MediaWiki format template
    timeTuple = time.strptime(rcstart, mwTimeFormat) # Convert MediaWiki time format to a Python tuple.
    plusSecond = datetime(*timeTuple[:7]) + timedelta(seconds=1) # Adding  a second.
    self.rcstart = plusSecond.strftime(mwTimeFormat)

if __name__ == '__main__':
  try:
    bot = VandalismBot()
    bot.run()
  finally:
    wikipedia.stopme()
